/** @file gui_widget.cpp
    @brief A general widget */

#include "gui/gui_widget.h"

using namespace G3;

//==============================================================================
// Constructors
//==============================================================================
G3::GUI::WIDGET::WIDGET () {
    Visible = true;
	Enabled = true;
    State = W_UP;
}

//==============================================================================
// Destructors
//==============================================================================
G3::GUI::WIDGET::~WIDGET () {
    Clear ();
}

//==============================================================================
// = operators
//==============================================================================
G3::GUI::WIDGET& G3::GUI::WIDGET::operator= (const WIDGET &aWidget) {
    Visible = aWidget.Visible;
    Rect = aWidget.Rect;
    Children = aWidget.Children;
    State = aWidget.State;
    Enabled = aWidget.Enabled;

    return *this;
}

//==============================================================================
// Recursive hide
//==============================================================================
void G3::GUI::WIDGET::Hide () {
    Visible = false;

    for (int i=0; i<(int)Children.size (); i++) {
        if (Children [i] != NULL)
            Children [i]->Hide ();
    }
}

//==============================================================================
// And show
//==============================================================================
void G3::GUI::WIDGET::Show () {
    Visible = true;

    for (int i=0; i<(int)Children.size (); i++) {
        if (Children [i] != NULL)
            Children [i]->Show ();
    }
}

//==============================================================================
// Recursive enable
//==============================================================================
void G3::GUI::WIDGET::Enable () {
    Enabled = true;

    for (int i=0; i<(int)Children.size (); i++) {
        if (Children [i] != NULL)
            Children [i]->Enable ();
    }
}

//==============================================================================
// And disable
//==============================================================================
void G3::GUI::WIDGET::Disable () {
    Enabled = false;

    for (int i=0; i<(int)Children.size (); i++) {
        if (Children [i] != NULL)
            Children [i]->Disable ();
    }
}

//==============================================================================
// Sets the widget's state
//==============================================================================
void G3::GUI::WIDGET::SetState (WIDGET_STATE newState) {
    State = newState;

    for (int i=0; i<(int)Children.size (); i++) {
        Assert (Children [i], "WIDGET::FitChildren: Invalid child pointer");

        Children [i]->SetState (newState);
    }
}

//==============================================================================
// Fits widget to its children
//==============================================================================
void G3::GUI::WIDGET::FitChildren () {
    Rect.Reset ();
    ExpandByChildren ();
}

//==============================================================================
// Expands the widget if its children occupy a larger area than the widget itself
//==============================================================================
void G3::GUI::WIDGET::ExpandByChildren () {
    for (int i=0; i<(int)Children.size (); i++) {
        Assert (Children [i], "WIDGET::FitChildren: Invalid child pointer");

        Rect.Expand (Children [i]->Rect);
    }
}

//==============================================================================
// Moves the widget
//==============================================================================
void G3::GUI::WIDGET::SetPosition (const G3::VEC2I &aPos) {
    Rect.Right += aPos.x - Rect.Left;
    Rect.Bottom += aPos.y - Rect.Top;
    Rect.Left = aPos.x;
    Rect.Top = aPos.y;

    // Also move the widget's children
    for (int i=0; i<(int)Children.size (); i++) {
        Assert (Children [i], "WIDGET::SetPosition: Child is a NULL pointer");

        Children [i]->SetPosition (VEC2I (Children [i]->Rect.Left + aPos.x, Children [i]->Rect.Top + aPos.y));
    }
}

//==============================================================================
// Performs a mousecheck on the widget
//==============================================================================
G3::GUI::WIDGET *G3::GUI::WIDGET::CheckMouse (INPUT *Input, WIDGET_STATE Check) {
    Assert (Input, "WIDGET::CheckMouse: Input argument is a NULL pointer");

	// If we aren't enabled, the mouse clicks pass right through us
	if (!Enabled)
		return NULL;

    WIDGET *TheCause = NULL;

	// Did the mouseclick hit us?
    if (Input->Mouse.Position.x >= Rect.Left &&
        Input->Mouse.Position.x <= Rect.Right &&
        Input->Mouse.Position.y >= Rect.Top &&
        Input->Mouse.Position.y <= Rect.Bottom) {

        // Perform a recursive check on its children aswell
        for (int i=0; i<(int)Children.size (); i++) {
            Assert (Children [i], "WIDGET::FitChildren: Invalid child pointer");

            TheCause = Children [i]->CheckMouse (Input, Check);

            if (TheCause != NULL && !TheCause->IsStatic ())
                break;
            else
                TheCause = NULL;
        }

        // No children are responsible for the event?
        if (TheCause == NULL) {
			// Then it must be us
            TheCause = this;
			// What was checked?
            switch (Check) {
                case W_OVER:
                    if (State == W_UP)
                        SigMouseOver (Input, this);

                    State = W_OVER;
                    break;

                case W_DOWN:
                    if (State == W_OVER || State == W_UP)
                        SigMouseDown (Input, this);

                    State = W_DOWN;
                    break;

                case W_UP:
                    if (State == W_DOWN) {
                        SigMouseClicked (Input, this);
                        SigMouseUp (Input, this);
                    }

                    State = W_OVER;
                    break;
            }
        }

        // We caught the event
        return TheCause;
    } else {
        switch (State) {
            case W_OVER:
                SigMouseOut (Input, this);
                break;

            case W_DOWN:
                SigMouseUp (Input, this);
                break;
        }

        SetState (W_UP);
    }

    // The event missed us
    return NULL;
}

//==============================================================================
// Attaches a widget to the children list
//==============================================================================
void G3::GUI::WIDGET::AttachWidget (WIDGET *widget) {
    Assert (widget, "WIDGET::AttachWidget: the widget argument is a NULL pointer..\n");

    Children.push_back (widget);
}

//==============================================================================
// Clears up the widget's class and its children
//==============================================================================
void G3::GUI::WIDGET::Clear () {
    Visible = false;

    // Clear the signals
    ConnMouseOver.clear ();
    ConnMouseOut.clear ();
    ConnMouseDown.clear ();
    ConnMouseUp.clear ();
    ConnMouseClicked.clear ();

    // Perform a recursive clear on our children aswell
    for (int i=0; i<(int)Children.size (); i++) {
        if (Children [i] != NULL)
            Children [i]->Clear ();
    }

    Children.clear ();
}

//==============================================================================
// Sets up a callback for moving mouse over a widget
//==============================================================================
G3::GUI::WIDGET::SIG_CONNECTION G3::GUI::WIDGET::doOnMouseOver (NOTIFY_HANDLER aNotifyHandler) {
	Assert (aNotifyHandler, "INPUT::WIDGET::doOnMouseOver: aNotifyHandler argument is a NULL pointer");

	SIG_CONNECTION c = SigMouseOver.connect (aNotifyHandler);
	ConnMouseOver.push_back (c);

	return c;
}

//==============================================================================
// Sets up a callback for moving mouse off of a widget
//==============================================================================
G3::GUI::WIDGET::SIG_CONNECTION G3::GUI::WIDGET::doOnMouseOut (NOTIFY_HANDLER aNotifyHandler) {
	Assert (aNotifyHandler, "INPUT::WIDGET::doOnMouseOut: aNotifyHandler argument is a NULL pointer");

	SIG_CONNECTION c = SigMouseOut.connect (aNotifyHandler);
	ConnMouseOut.push_back (c);

	return c;
}

//==============================================================================
// Sets up a callback for a mouse button down
//==============================================================================
G3::GUI::WIDGET::SIG_CONNECTION G3::GUI::WIDGET::doOnMouseDown (NOTIFY_HANDLER aNotifyHandler) {
	Assert (aNotifyHandler, "INPUT::WIDGET::doOnMouseDown: aNotifyHandler argument is a NULL pointer");

	SIG_CONNECTION c = SigMouseDown.connect (aNotifyHandler);
	ConnMouseDown.push_back (c);

	return c;
}

//==============================================================================
// Sets up a callback for a mouse button release
//==============================================================================
G3::GUI::WIDGET::SIG_CONNECTION G3::GUI::WIDGET::doOnMouseUp (NOTIFY_HANDLER aNotifyHandler) {
	Assert (aNotifyHandler, "INPUT::WIDGET::doOnMouseUp: aNotifyHandler argument is a NULL pointer");

	SIG_CONNECTION c = SigMouseUp.connect (aNotifyHandler);
	ConnMouseUp.push_back (c);

	return c;
}

//==============================================================================
// Sets up a callback for a mouseclick
//==============================================================================
G3::GUI::WIDGET::SIG_CONNECTION G3::GUI::WIDGET::doOnMouseClicked (NOTIFY_HANDLER aNotifyHandler) {
	Assert (aNotifyHandler, "INPUT::WIDGET::doOnClicked: aNotifyHandler argument is a NULL pointer");

	SIG_CONNECTION c = SigMouseClicked.connect (aNotifyHandler);
	ConnMouseClicked.push_back (c);

	return c;
}
